package com.zis.platform.common.mybatis;

import java.util.Map;
import java.util.Properties;

import net.sf.jsqlparser.JSQLParserException;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.MappedStatement.Builder;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.util.CollectionUtils;

import com.zis.platform.common.authentication.UserToken;
import com.zis.platform.common.util.CacheCoreUtil;
import com.zis.platform.core.entity.DataAuthStrategyEntity;

/**
 * <b>说明：</b>数据权限拦截器
 * 
 * @ClassName: DataInterceptor
 * @author zhaohaitao(2543)
 * @date 2015-7-10 上午10:13:50
 */
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
    RowBounds.class, ResultHandler.class})})
public class DataInterceptor extends DataAccessFilter implements Interceptor
{
    private static int MAPPED_STATEMENT_INDEX = 0;
    
    private static int PARAMETER_INDEX = 1;
    
    @Override
    public Object intercept(Invocation invocation)
        throws Throwable
    {
        Map<String, DataAuthStrategyEntity> allDataAuthStrategy = CacheCoreUtil.getAllDataAuthStrategy();
        // 判断系统是否启动
        if (CacheCoreUtil.getSystemStartState() && !CollectionUtils.isEmpty(allDataAuthStrategy))
        {
            UserToken userToken = getUserToken();
            // 判断当前用户登录信息是否有效
            if (userToken != null && !userToken.getUser().isSupperManager())
            {
                processIntercept(invocation.getArgs());
            }
        }
        return invocation.proceed();
    }
    
    @Override
    public Object plugin(Object target)
    {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties)
    {
    }
    
    /**
     * 获取当前登录用户
     * 
     * @return
     */
    private UserToken getUserToken()
    {
        Subject subject;
        try
        {
            subject = SecurityUtils.getSubject();
        }
        catch (Exception e)
        {
            return null;
        }
        UserToken userToken = (UserToken)subject.getPrincipal();
        return userToken;
    }
    
    /**
     * 执行拦截
     * 
     * @param queryArgs
     * @throws JSQLParserException
     */
    
    private void processIntercept(final Object[] queryArgs)
        throws JSQLParserException
    {
        MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX];
        Object parameter = queryArgs[PARAMETER_INDEX];
        BoundSql boundSql = ms.getBoundSql(parameter);
        String sql = boundSql.getSql();
        String permissionSql = checkDataPermissions(sql, getUserToken().getDataPermission());
        BoundSql newBoundSql =
            new BoundSql(ms.getConfiguration(), permissionSql, boundSql.getParameterMappings(),
                boundSql.getParameterObject());
        MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
        queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
    }
    
    /**
     * 创建一个新的映射语句
     * 
     * @param ms
     * @param newSqlSource
     * @return
     */
    private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource)
    {
        Builder builder =
            new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        String[] params = ms.getKeyProperties();
        if (params != null)
        {
            for (String str : ms.getKeyProperties())
            {
                builder.keyProperty(str);
            }
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }
    
    public static class BoundSqlSqlSource implements SqlSource
    {
        
        private BoundSql boundSql;
        
        public BoundSqlSqlSource(BoundSql boundSql)
        {
            this.boundSql = boundSql;
        }
        
        public BoundSql getBoundSql(Object parameterObject)
        {
            return boundSql;
        }
    }
}
